<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>实现race方法</title>
  </head>
  <body>
    <script>
      class LPromise {
        constructor(callbackFn) {
          this['[[PromiseState]]'] = 'pending'
          this['[[PromiseResult]]'] = undefined
          this.cbResolveQueue = []
          this.cbRejectQueue = []
          callbackFn(this.#resolve.bind(this), this.#reject.bind(this))
        }
        #resolve(res) {
          this['[[PromiseState]]'] = 'fulfilled'
          this['[[PromiseResult]]'] = res
          const run = () => {
            let cbFn
            while ((cbFn = this.cbResolveQueue.shift())) {
              cbFn && cbFn(res)
            }
          }
          const ob = new MutationObserver(run)
          ob.observe(document.body, { attributes: true })
          document.body.setAttribute('lpromise', 'layouwen')
        }
        #reject(err) {
          this['[[PromiseState]]'] = 'reject'
          this['[[PromiseResult]]'] = err
          const run = () => {
            let cbFn
            while ((cbFn = this.cbRejectQueue.shift())) {
              cbFn && cbFn(err)
            }
          }
          const ob = new MutationObserver(run)
          ob.observe(document.body, { attributes: true })
          document.body.setAttribute('lpromise', 'layouwen')
        }
        then(onResolve, onReject) {
          return new LPromise((resolve, reject) => {
            const cbResolve = res => {
              const resolveRes = onResolve && onResolve(res)
              if (resolveRes instanceof LPromise) {
                resolveRes.then(resolve)
              } else {
                resolve(res)
              }
            }
            this.cbResolveQueue.push(cbResolve)
            const cbReject = err => {
              onReject && onReject(err)
              reject(err)
            }
            this.cbRejectQueue.push(cbReject)
          })
        }
        catch(err) {
          this.then(undefined, err)
        }
        finally(callback) {
          this.then(callback, callback)
        }
        static resolve(res) {
          return new LPromise(resolve => resolve(res))
        }
        static reject(err) {
          return new LPromise((undefined, reject) => reject(err))
        }
        /* new content start */
        static race(promiseArr) {
          return new LPromise((resolve, reject) => {
            let isContinue = true
            promiseArr.forEach(promise => {
              promise.then(
                res => {
                  if (isContinue) {
                    isContinue = false
                    resolve(res)
                  }
                },
                err => {
                  if (isContinue) {
                    isContinue = false
                    reject(err)
                  }
                }
              )
            })
          })
        }
        /* new content end */
      }
      const p1 = new LPromise((resolve, reject) => setTimeout(() => resolve(1), 200))
      const p2 = new LPromise((resolve, reject) => setTimeout(() => reject(2), 1000))
      const p3 = new LPromise((resolve, reject) => setTimeout(() => resolve(3), 3000))
      LPromise.race([p1, p2, p3]).then(
        res => console.log('res', res),
        err => console.log('err', err)
      )
    </script>
  </body>
</html>
